package main

import (
	"fmt"
	"github.com/astaxie/beego/logs"
	"net"
	"time"
)

//全局变量
var (
	//全局日志
	log *logs.BeeLogger
	//所有的请求者 ，使用map 进行维护
	clientMap map[string]ClientReq
	//所有消息传送通道
	mesg chan string
)

type ClientReq struct {
	Name string      //请求名称
	C    chan string //接收自己消息的管道
	Addr string      //地址的
}

//日志配置
func init() {
	//创建logger对象
	log = logs.NewLogger()
	//log输出到控制台
	log.SetLogger(logs.AdapterConsole)
	//log输出的文件
	log.SetLogger(logs.AdapterFile, `{"filename":"./chatRoomTest/logs/server.log" ,"level":6,"maxlines":0,"maxsize":0,"daily":true,"maxdays":1}`)
}

func main() {
	log.Info("server is starting")
	//使用 9000 端口进行监听
	listener, e := net.Listen("tcp", ":9000")
	//错误判断
	checkErr(e, "server listener")
	//不用监听时候 进行关闭
	defer listener.Close()
	//消息转发核心
	go WriteMesToAllClient()
	//初始化 客户端的clientMap
	clientMap = make(map[string]ClientReq)
	//开始等待客户端连接
	for {
		conn, e := listener.Accept()

		if e != nil {
			log.Info("accept err=", e)
			continue
		}
		//多携程 处理
		go handleConn(conn)
	}

}

//连接处理
func handleConn(con net.Conn) {
	addinfo := con.RemoteAddr().String()
	//对应的客户端实体
	cliReq := ClientReq{
		Name: addinfo,
		C:    make(chan string),
		Addr: addinfo,
	}
	//将当前连接加入 clent的map中
	clientMap[addinfo] = cliReq
	log.Info("%v connected", addinfo)
	//上线告知---全员
	mesg <- fmt.Sprintf("[ %s ] :login successs", cliReq.Name)

	//判断是否退出
	isQuit := make(chan bool)
	//判断是否超时
	isTimeout := make(chan bool)
	//处理客户端的消息接收
	go func() {
		for {
			//读取客户端的数据
			tmp := make([]byte, 1<<10)
			n, err := con.Read(tmp)
			if err != nil {
				log.Info("err=%v", err)
				isQuit <- true
				break
			}
			//客户端的数据
			msg := string(tmp[:n])
			//将客户端的数据 ，放在 消息转发通道中
			mesg <- fmt.Sprintf("client :%v,mes :%v ", cliReq.Name, msg)
			isTimeout <- false
		}
	}()
	//处理 客户端发消息
	go WriteMes(cliReq, con)
	for {
		select {
		case <-isQuit:
			//map中移除
			delete(clientMap, addinfo)
			mesg <- fmt.Sprintf("%s levave out", addinfo)
			break
		case <-isTimeout:
			//从超时chan中获取数据（如果在1分钟之内的有数据则执行该模块，如果没有就超时了）
		case <-time.After(time.Second * 60):
			delete(clientMap, addinfo)
			mesg <- fmt.Sprintf("%s timeout out", addinfo)
			con.Close()
			break
		}

	}

	////阻值该协程 挂了
	//select {}
}

//消息写给 该连接的客户端(客户端的接受器)
func WriteMes(req ClientReq, conn net.Conn) {
	for reqMes := range req.C {
		log.Info("分别写给自己的客户端=%v,地址=%v", reqMes, conn.RemoteAddr().String())
		conn.Write([]byte(reqMes))
	}
}

//核心消息转发 给客户端
func WriteMesToAllClient() {
	mesg = make(chan string)
	for {
		//如果有消息 转发给所有的client
		mes := <-mesg
		log.Info("receive data=%v", mes)
		for i, cliReq := range clientMap {
			log.Info("转发对象= %v，信息为=%v", i, mes)
			cliReq.C <- mes
		}
	}
}

//错误的检查
func checkErr(err error, mes string) {
	if err != nil {
		log.Info(mes+",err=", err)
		return
	}
}
